package graduation.project.mutant.strategy;

/***
 * here mutants are composed by the cells with HD = -w;
 */
import graduation.project.body.Expression;
import graduation.project.body.Mutants;
import graduation.project.body.OriginExpression;
import graduation.project.exception.NonExpressionException;
import graduation.project.utilities.Utility;

import java.util.ArrayList;

public class HDMutantsGen implements MutantsGenerator {
	
	private int HDValue = -3;
	
	public void setHDValue(int HDValue){
		this.HDValue = HDValue;
	}
	public int getHDValue(){
		return HDValue;
	}

	public Mutants generateMutants(String expression, int mutantsNum) {
		
		try{
			OriginExpression originEx = new OriginExpression(expression);
			return generateMutants(originEx, mutantsNum);
		}catch(NonExpressionException e){
			System.out.println("the String is invalid expression.");
			e.printStackTrace();
		}
		return null;
	}

	public Mutants generateMutants(OriginExpression originEx, int num) {

		if(HDValue == 0){
			System.out.println("Please assign the value to the HDValue.");
			return null;
		}
		
		int count = 0;
		String model = originEx.getModel();
		Mutants result = new Mutants(originEx.getExpression(), "HD_" + HDValue);
		ArrayList<Expression> mutantSet = new ArrayList<Expression>();
		int[] hdCells = OriginExpression.getHDSetPoints(originEx, HDValue);
		
		String[][] mutantArr = getAllMutantsonHD(hdCells, model);
		if(mutantArr != null){
			for(int i = mutantArr.length - 1; i >= 0; i--){
				for(int j = 0; j < mutantArr[i].length; j++){
					try {
						mutantSet.add(new Expression(mutantArr[i][j]));
					} catch (NonExpressionException e) {
						System.out.println("the String is not valid expression");
						e.printStackTrace();
					}
					if(num != -1 && ++count > num)
						break;
				}
				if(num != -1 && count > num)
					break;
			}
		}
		
		result.setMutants(mutantSet);
		return result;
	}
	
	private String[][] getAllMutantsonHD(int[] hdArr, String model){
		if(hdArr == null)
			return null;
		int layer = (int)Math.ceil(Math.log(hdArr.length) / Math.log(2));
		ArrayList<String> allMutants = new ArrayList<String>();
		int[] beginPos = new int[layer * 2];
		int indexforBeginPos = 0;
		beginPos[indexforBeginPos++] = 0;
		for(int i = 0; i < hdArr.length; i++)
			allMutants.add(Utility.intoString(hdArr[i], model));
		beginPos[indexforBeginPos++] = hdArr.length;
		
		int index = hdArr.length; // as count of allMutants
		int begin, end;
		String joinTerm;
		while(true){
			begin = beginPos[indexforBeginPos - 2];
			end = beginPos[indexforBeginPos - 1];
			if(begin == end)
				break;
			for(int i = begin; i < end; i++)
				for(int j = i + 1; j < end; j++)
					if(getDifferofTwoString(allMutants.get(i), allMutants.get(j)) == 1){
						boolean isSame = false;
						joinTerm = joinTwoString(allMutants.get(i), allMutants.get(j));
						for(int k = end; k < index; k++)
							if(allMutants.get(k).equals(joinTerm)){
								isSame = true;
								break;
							}
						if( ! isSame){
							allMutants.add(joinTerm);
							index++;
						}
					}
				
			beginPos[indexforBeginPos++] = index;
		}
		
		int count = 0;
		for(int i = 0; i < beginPos.length; i++){
			if(beginPos[i] == beginPos[i + 1])
				break;
			count++;
		}
		
		String[][] result = new String[count][];
		for(int i = 0; i < result.length; i++)
			result[i] = new String[beginPos[i + 1] - beginPos[i]];
		
		index = 0;
		for(int i = 0; i < result.length; i++)
			for(int j = 0; j < result[i].length; j++){
				result[i][j] = allMutants.get(index);
				index++;
			}
		return result;
	}
	
	private int getDifferofTwoString(String s1, String s2){
		
		int dif = 0;
		boolean not = false;
		char lit;
		int pos;
		for(int i = 0; i < s1.length(); i++){
			lit = s1.charAt(i);
			if(lit == '!'){
				not = true;
				continue;
			}
			pos = s2.indexOf(lit);
			if(pos < 0){
				dif = -1;
				break;
			}
			if((not && ((pos == 0) || (s2.charAt(pos - 1) != '!')))  ||
					((not == false) && (pos != 0 && s2.charAt(pos - 1) == '!')))
				dif++;
		}
		return dif;
	}
	private String joinTwoString(String s1, String s2){
		
		StringBuffer result = new StringBuffer();
		result.append(s1);
		char lit;
		for(int i = 0; i < s1.length(); i++){
			lit = s1.charAt(i);
			if(lit == '!'){
				lit = s1.charAt(i + 1);
				if(s2.indexOf(lit) == 0 || (s2.charAt(s2.indexOf(lit) - 1) != '!')){
					result.delete(i, i + 2);
					break;
				}
				i++;
			}else{
				if(s2.indexOf(lit) != 0 && (s2.charAt(s2.indexOf(lit) - 1) == '!')){
					result.deleteCharAt(i);
					break;
				}
			}
		}
		
		return result.toString();
	}

	public static void main(String[] args){
		
	}
}
